/*
 * Copyright (c) 2001 Boris Pruessmann and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    Boris Pruessmann - Initial implementation.
 */
package net.sourceforge.p4eclipse.ui;

import java.lang.reflect.InvocationTargetException;

import net.sourceforge.p4eclipse.core.Policy;
import net.sourceforge.p4eclipse.core.resources.LocalFile;
import net.sourceforge.p4eclipse.core.resources.LocalResource;
import net.sourceforge.p4eclipse.core.resources.RemoteFolder;
import net.sourceforge.p4eclipse.core.resources.RemoteResource;
import net.sourceforge.p4eclipse.core.syncinfo.ResourceSyncInfo;
import org.eclipse.compare.CompareConfiguration;
import org.eclipse.compare.CompareEditorInput;
import org.eclipse.compare.ITypedElement;
import org.eclipse.compare.ResourceNode;
import org.eclipse.compare.structuremergeviewer.Differencer;
import org.eclipse.compare.structuremergeviewer.IStructureComparator;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;

/**
  * @version 	1.0
  * @author		<a href="mailto:bpruessmann@users.sourceforge.net">Boris Pruessmann</a>
  */
public class PerforceCompareEditorInput extends CompareEditorInput
{
	private ITypedElement left;
	private ITypedElement right;
	private ITypedElement ancestor;

	// comparison constants
	private static final int NODE_EQUAL = 0;
	private static final int NODE_NOT_EQUAL = 1;
	private static final int NODE_UNKNOWN = 2;
	
	/**
	 * Creates a new PerforceCompareEditorInput.
	 */
	public PerforceCompareEditorInput(ITypedElement left, ITypedElement right) 
	{
		this(left, right, null);
	}
	
	/**
	 * Creates a new PerforceCompareEditorInput.
	 */
	public PerforceCompareEditorInput(ITypedElement left, ITypedElement right, ITypedElement ancestor) 
	{
		super(new CompareConfiguration());
		this.left = left;
		this.right = right;
		this.ancestor = ancestor;
	}	

	/*
	 * @see CompareEditorInput#prepareInput(IProgressMonitor)
	 */
	protected Object prepareInput(IProgressMonitor monitor)
		throws InvocationTargetException, InterruptedException
	{
		boolean threeWay = ancestor != null;
		if (right == null || left == null)
			return null;
			
		initLabels();
			
		Differencer differencer = 
			new Differencer()
			{
				protected boolean contentsEqual(Object input1, Object input2)
				{
					int compare = teamEqual(input1, input2);
					if (compare == NODE_EQUAL)
						return true;
					else
					if (compare == NODE_NOT_EQUAL) 
						return false;					
					
					return super.contentsEqual(input1, input2);
				}
				
				protected void updateProgress(IProgressMonitor progressMonitor, Object node) 
				{
					super.updateProgress(progressMonitor, node);
					progressMonitor.worked(1);
				}
				
				protected Object[] getChildren(Object input) 
				{
					if (input instanceof IStructureComparator) 
					{
						Object[] children= ((IStructureComparator)input).getChildren();
						if (children != null)
							return children;
					}
					return null;
				}				
			};
			
		try 
		{
			monitor.beginTask(Policy.bind("PerforceCompareEditorInput.comparing"), 30); //$NON-NLS-1$
					
			IProgressMonitor sub = new SubProgressMonitor(monitor, 10);
			try 
			{
				sub.beginTask(Policy.bind("PerforceCompareEditorInput.comparing"), 100); //$NON-NLS-1$
				return differencer.findDifferences(threeWay, sub, null, ancestor, left, right);
			} 
			finally 
			{
				sub.done();
			}
		}
		catch (OperationCanceledException e) 
		{
			throw new InterruptedException(e.getMessage());
		}
		finally
		{
			monitor.done();
		}
	}

	/**
	  * Sets up the title and pane labels for the comparison view.
	  */
	private void initLabels()
	{
		CompareConfiguration cc = (CompareConfiguration) getCompareConfiguration();
	
		String leftLabel = getLabel(left);
		cc.setLeftLabel(leftLabel);
		cc.setLeftImage(left.getImage());
	
		String rightLabel = getLabel(right);
		cc.setRightLabel(rightLabel);
		cc.setRightImage(right.getImage());
	
		String title = null;
		if (ancestor != null) 
		{
			cc.setAncestorLabel(getLabel(ancestor));
			cc.setAncestorImage(ancestor.getImage());
			title = Policy.bind("PerforceCompareEditorInput.titleAncestor", new Object[] {guessResourceName(), getVersionLabel(ancestor), getVersionLabel(left), getVersionLabel(right)} ); //$NON-NLS-1$
		} 
		else 
		{
			String leftName = null;
			if (left != null) 
				leftName = left.getName();
				
			String rightName = null;
			if (right != null) 
				rightName = right.getName();
				
			if (leftName != null && !leftName.equals(rightName)) 
				title = Policy.bind("PerforceCompareEditorInput.titleNoAncestorDifferent", new Object[] {leftName, getVersionLabel(left), rightName, getVersionLabel(right)} ); //$NON-NLS-1$
			else 
				title = Policy.bind("PerforceCompareEditorInput.titleNoAncestor", new Object[] {guessResourceName(), getVersionLabel(left), getVersionLabel(right)} ); //$NON-NLS-1$

		}
		
		setTitle(title);
	}

	/**
	  * Returns the label for the given input element.
	  */
	private String getLabel(ITypedElement element) 
	{
		return element.getName();
	}

	/*
	 * Returns a guess of the resource name being compared, for display
	 * in the title.
	 */
	private String guessResourceName() 
	{
		if (left != null) 
			return left.getName();
		
		if (right != null) 
			return right.getName();
		
		if (ancestor != null) 
			return ancestor.getName();
		
		return ""; //$NON-NLS-1$
	}

	/**
	  * Returns the label for the given input element.
	  */
	private String getVersionLabel(ITypedElement element) 
	{
		if (element instanceof ResourceNode) 
			return Policy.bind("PerforceCompareEditorInput.workspaceLabel"); //$NON-NLS-1$
		
		if (element instanceof PerforceResourceEditionNode) 
		{
			RemoteResource edition = ((PerforceResourceEditionNode)element).getRemoteResource();
			if (edition instanceof RemoteFolder)
				return ((RemoteFolder)edition).getRevision();
			else
				return "#" + edition.getSyncInfo().getHeadRev().toString(); //$NON-NLS-1$
		}
		return element.getName();
	}

	/*
	 * This is only a compare viewer.
	 */
	public boolean isSaveNeeded()
	{
		return false;
	}
	
	/**
	 * Compares two nodes to determine if they are equal.  Returns NODE_EQUAL
	 * of they are the same, NODE_NOT_EQUAL if they are different, and
	 * NODE_UNKNOWN if comparison was not possible.
	 */
	private int teamEqual(Object left, Object right) 
	{
		boolean threeWay = ancestor != null;
		
		ResourceNode localFileNode = null;
		if (threeWay)
		{
			if (right instanceof ResourceNode)
				localFileNode = (ResourceNode)right;			
		}
		else
		{
			if (left instanceof ResourceNode)
				localFileNode = (ResourceNode)left;
		}
		
		if (localFileNode != null)
		{
			if (localFileNode.getResource().getType() == IResource.FILE)
			{
				LocalFile localFile = (LocalFile)LocalResource.createFromResource(localFileNode.getResource());
				if (!localFile.isCheckedOut() && !localFile.isDirty() && !localFile.isOutOfDate())
					return NODE_EQUAL;
			}
		}
		
		if (left instanceof PerforceResourceEditionNode && right instanceof PerforceResourceEditionNode)
		{
			RemoteResource leftResource  = ((PerforceResourceEditionNode)left).getRemoteResource();
			RemoteResource rightResource = ((PerforceResourceEditionNode)right).getRemoteResource();
			
			ResourceSyncInfo leftSyncInfo = leftResource.getSyncInfo();
			ResourceSyncInfo rightSyncInfo = rightResource.getSyncInfo();
			
			if (leftSyncInfo != null && rightSyncInfo != null)
			{
				if (leftSyncInfo.getHeadRev().equals(rightSyncInfo.getHeadRev()))
					return NODE_EQUAL;				
			}
		}
		
		return NODE_UNKNOWN;
	}
	
}
